﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Drawing;
using System.IO.Compression;
using System.Text.RegularExpressions;
using GBPVR.Public;

namespace GBPVRLibrary.MetadataLibrary
{
    public class WebSiteHelper
    {
        private static int Timeout = 60 * 1000; // 1 minute

        public static WebProxy Proxy
        {
            get
            {
                // todo, read proxy settings from gbpvrs config file.
                return new WebProxy();
            }
        }

        public static string ReadGetResponse(ref string url, List<Cookie> cookies)
        {
            int count = 0;
            Stream responseStream = null;
            string CacheFile = CacheFilename(url);
            if (File.Exists(CacheFile))
                return ReadInFile(CacheFile);
                
            while (count++ < 3)
            {
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
                request.CookieContainer = new CookieContainer();
                if (cookies != null)
                {
                    foreach (Cookie cookie in cookies)
                        request.CookieContainer.Add(cookie);
                }
                request.Timeout = Timeout;
                request.AllowAutoRedirect = true;
                request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)";

                request.Proxy = WebSiteHelper.Proxy;

                HttpWebResponse response;
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                    url = response.ResponseUri.AbsoluteUri;
                }
                catch (WebException)
                {
                    if (count < 3)
                        System.Threading.Thread.Sleep(5000); // let the server wait a bit
                    continue;
                }
                try
                {
                    responseStream = response.GetResponseStream();
                    if (response.ContentEncoding.ToLower().Contains("gzip"))
                        responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
                    else if (response.ContentEncoding.ToLower().Contains("deflate"))
                        responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
                    Encoding encoding = Encoding.Default;
                    try
                    {
                        encoding = System.Text.Encoding.GetEncoding(response.ContentEncoding);
                    }
                    catch (Exception) 
                    {
                        Match match = Regex.Match(response.ContentType, "(?<=(charset=))[^;]*(?=(;|$))", RegexOptions.IgnoreCase);
                        if (match.Success)
                        {
                            try
                            {
                                encoding = System.Text.Encoding.GetEncoding(match.Value);
                            }
                            catch (Exception) { }
                        }
                    }
                    using (StreamReader Reader = new StreamReader(responseStream, encoding))
                    {
                        string page = Reader.ReadToEnd();
                        try
                        {
                            using (StreamWriter writer = new StreamWriter(CacheFile))
                            {
                                writer.Write(page);
                                writer.Flush();
                                writer.Close();
                            }
                        }
                        catch (Exception) { }
                        return page;
                    }
                }
                catch (Exception) { }
                finally
                {
                    if (responseStream != null)
                    {
                        responseStream.Dispose();
                        responseStream.Close();
                    }
                    response.Close();
                }
            }
            return null;
        }

        /// <summary>
        /// Saves a stream to a path specified
        /// </summary>
        /// <param name="Url">the url of the item to download</param>
        /// <param name="Path">the path to save the file to</param>
        /// <returns>true if successful, otherwise false</returns>
        public static bool DownloadBinaryStream(string Url, string Path)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(Url);            
            request.Timeout = Timeout;
            request.AllowAutoRedirect = true;
            request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");

            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)";

            request.Proxy = WebSiteHelper.Proxy;

            HttpWebResponse response;
            try
            {
                response = (HttpWebResponse)request.GetResponse();

                byte[] buffer = new byte[32768];
                using (Stream input = response.GetResponseStream())
                {
                    using (FileStream output = new FileStream(Path, FileMode.Create))
                    {
                        int bytesRead;
                        while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            output.Write(buffer, 0, bytesRead);
                        }
                    }
                }

                return true;
            }
            catch (Exception ex) 
            {
                Logger.Verbose("Failed to download file: " + ex.Message + Environment.NewLine + ex.StackTrace);
                return false; 
            }
        }

        private static Image DownloadImageAttempt(string ImageUrl, int Attempt, long LastFileSize)
        {
            string cacheFile = CacheFilename(ImageUrl, "image");
            System.Uri uri = new System.Uri(ImageUrl);

            Stream responseStream = null;
            try
            {
                HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(ImageUrl);
                request.Timeout = Timeout;
                request.AllowAutoRedirect = true;
                request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate");
                request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)";

                request.Proxy = WebSiteHelper.Proxy;

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();

                responseStream = response.GetResponseStream();
                if (response.ContentEncoding.ToLower().Contains("gzip"))
                    responseStream = new GZipStream(responseStream, CompressionMode.Decompress);
                else if (response.ContentEncoding.ToLower().Contains("deflate"))
                    responseStream = new DeflateStream(responseStream, CompressionMode.Decompress);
                long expectedSize = response.ContentLength;
                Image img = null;
                try
                {
                    img = Image.FromStream(responseStream);
                    img.Save(cacheFile);
                    long filesize = new FileInfo(cacheFile).Length;
                    if (expectedSize > 0 && filesize < expectedSize - 256 && !(LastFileSize > 0 && LastFileSize == filesize))
                    {
                        if (Attempt < 3)
                        {
                            File.Delete(cacheFile);
                            img.Dispose();
                            return DownloadImageAttempt(ImageUrl, Attempt + 1, filesize);
                        }
                    }
                }
                catch (Exception) { };
                return img;
            }
            catch (Exception) { return null; }
            finally
            {
                if (responseStream != null)
                {
                    responseStream.Dispose();
                    responseStream.Close();
                }
            }
        }

        /// <summary>
        /// Downloads an image from a specified Url
        /// </summary>
        /// <param name="ImageUrl">the Url of the Image</param>
        /// <returns>the downloaded image, or null if it fails to load</returns>
        public static Image DownloadImage(string ImageUrl)
        {
            if (String.IsNullOrEmpty(ImageUrl))
                return null;
            string cacheFile = CacheFilename(ImageUrl, "image");
            FileInfo file = new FileInfo(cacheFile);
            if(file.Exists)
            {
                try
                {
                    return Image.FromFile(file.FullName);                    
                }
                catch(Exception){}
            }
            return DownloadImageAttempt(ImageUrl, 0, 0);
        }

        public static string RemoveHTML(string instring)
        {
            if (String.IsNullOrEmpty(instring))
                return instring;

            string description = instring.Replace("<P>", "");
            try
            {
                description = description.Replace("<p>", "");
                description = description.Replace("<I>", "");
                description = description.Replace("</I>", "");
                description = description.Replace("<i>", "");
                description = description.Replace("</i>", "");
                description = description.Replace("<B>", "");
                description = description.Replace("</B>", "");
                description = description.Replace("<b>", "");
                description = description.Replace("</b>", "");
                description = description.Replace("&#34;", "\"");
                description = description.Replace("&nbsp;", " ");
                description = description.Replace("\n", "");
                description = description.Replace("\r", "");
                description = description.Replace("</TD>", "");
                description = description.Replace("</td>", "");
                description = description.Replace("</TR>", "");
                description = description.Replace("</tr>", "");
                description = description.Replace("&amp;", "&");
                description = description.Replace("&apos;", "'");
                description = description.Replace("â€¢", "•");

                while (description.IndexOf("<") >= 0 && description.IndexOf(">") > description.IndexOf("<"))
                {
                    string firstpart = description.Substring(0, description.IndexOf("<"));
                    string secondpart = description.Substring(description.IndexOf(">") + 1);
                    description = firstpart + secondpart;
                }

                if (description.IndexOf(">") >= 0)
                    description = description.Substring(description.IndexOf(">") + 1);
                if (description.IndexOf("<") >= 0)
                    description = description.Substring(0, description.IndexOf("<"));

                // Substitute all &#xx; ascii codes
                int firstPosition;
                while ((firstPosition = description.IndexOf("&#", 0)) != -1)
                {
                    try
                    {
                        string firstPart = description.Substring(0, firstPosition);
                        int secondPosition = description.IndexOf(";", firstPosition);
                        int asciiCode = Convert.ToInt16(description.Substring(firstPosition + 2, secondPosition - firstPosition - 2));
                        description = firstPart + Convert.ToChar(asciiCode) + description.Substring(secondPosition + 1);
                    }
                    catch (Exception) { break; }
                }


                description = System.Web.HttpUtility.HtmlDecode(description);
                return description.Trim();
            }
            catch (Exception) { return description.Trim(); }
        }

        public static string CacheFilename(string Url, string type)
        {
            string domain = null;
            Match match = Regex.Match(Url, @"(?<=(http(s)?://))[^/]*(?=/)", RegexOptions.IgnoreCase);
            if (match.Success)
                domain = match.Value;
            DirectoryInfo dir = new DirectoryInfo(GBPVRLibrary.UtilityLibrary.ComputerSystem.SystemInformation.GBPVRInstallPath);
            DirectoryInfo cachedir = new DirectoryInfo(dir.FullName + @"\temp\cache" + (String.IsNullOrEmpty(domain) ? "" : @"\" + domain) + (String.IsNullOrEmpty(type) ? "" : @"\" + type));
            if (!cachedir.Exists)
                cachedir.Create();

            System.Security.Cryptography.MD5CryptoServiceProvider x = new System.Security.Cryptography.MD5CryptoServiceProvider();
            byte[] bs = System.Text.Encoding.UTF8.GetBytes(Url);
            bs = x.ComputeHash(bs);
            System.Text.StringBuilder s = new System.Text.StringBuilder();
            foreach (byte b in bs)
            {
                s.Append(b.ToString("x2").ToLower());
            }
            bs = null;
            return String.Format(@"{0}\{1}", cachedir.FullName, s.ToString());
        }

        public static string CacheFilename(string Url)
        {
            return CacheFilename(Url, "text");
        }

        public static void DeleteCacheFile(string Url)
        {
            string cachefile = CacheFilename(Url);
            if (File.Exists(cachefile))
            {
                try
                {
                    File.Delete(cachefile);
                }
                catch (Exception) { }
            }
        }

        private static string ReadInFile(string Filename)
        {
            using (StreamReader reader = new StreamReader(Filename))
            {
                return reader.ReadToEnd();
            }
        }
    }
}
